package com.ruoyi.system.es;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
import co.elastic.clients.elasticsearch._types.mapping.Property;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.search.Highlight;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.TrackHits;
import co.elastic.clients.elasticsearch.indices.CreateIndexResponse;
import co.elastic.clients.elasticsearch.indices.DeleteIndexResponse;
import co.elastic.clients.elasticsearch.indices.GetIndexResponse;
import co.elastic.clients.elasticsearch.indices.PutMappingResponse;
import co.elastic.clients.elasticsearch.snapshot.CreateSnapshotRequest;
import co.elastic.clients.elasticsearch.snapshot.CreateSnapshotResponse;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.client.RequestOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.*;



/**
 * es检索上下文
 */
@Slf4j
@Component
public class ESContext {


    @Autowired
    private ElasticsearchClient client;

    private static String KEYWORD = ".keyword";

    /**
     * 创建索引
     * @param index
     * @param mapping
     * @return
     */
    public boolean createIndex(String index, Map<String, Property> mapping) {
        try {
            log.info("创建es索引:{}，mapping映射为:{}", index, JSON.toJSONString(mapping));
            if (client.indices().exists(query -> query.index(index)).value()) {
                //索引已经存在
                return false;
            }
            CreateIndexResponse response;
            if (mapping.isEmpty()) {
                response = client.indices().create(c -> c.index(index));
            } else {
                response = client.indices().create(c -> c.index(index).mappings(m -> m.properties(mapping)));
            }
            return Boolean.TRUE.equals(response.acknowledged());
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除索引
     * @param index
     * @return
     */
    public boolean deleteIndex(String index) {
        try {
            log.info("删除es索引:{}", index);
            if (!client.indices().exists(query -> query.index(index)).value()) {
                //索引不存在
                return false;
            }
            DeleteIndexResponse response = client.indices().delete(c -> c.index(index));
            return response.acknowledged();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除文档
     * @param index
     * @return
     */
    public boolean deleteDocumentById(String index, String id) {
        try {
            log.info("删除es文档:{}-{}", index, id);
            if (!client.indices().exists(query -> query.index(index)).value()) {
                //索引不存在
                return false;
            }
            DeleteResponse response = client.delete(builder -> builder.index(index).id(id));
            return response.shards().successful().longValue() > 0;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除文档
     * @param index
     * @return
     */
    public boolean deleteDocumentAll(String index) {
        try {
            log.info("删除es文档:{}", index);
            if (!client.indices().exists(query -> query.index(index)).value()) {
                //索引不存在
                return false;
            }
            DeleteResponse response = client.delete(builder -> builder.index(index));
            return response.shards().successful().longValue() > 0;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 查询某个索引
     * @param index
     * @return
     */
    public GetIndexResponse getIndex(String index) {
        try {
            return client.indices().get(x -> x.index(index));
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 查询某个索引数据总数
     * @param index
     * @return
     */
    public Integer getIndexCount(String index) {
        try {
            SearchResponse<Object> response = client.search(x -> x.index(index), Object.class);
            return response.hits().hits().size();
        } catch (IOException e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 创建文档
     * @param index
     * @return
     */
    public Long addDocument(String index, String id, Map<String, Object> doc) {
        try {
            //log.info("添加es文档，索引为:{}，文档数据:{}",index,JSON.toJSONString(doc));
            IndexResponse response = client.index(x -> x.index(index).id(id).document(doc));
            return response.version();
        } catch (IOException e) {
            e.printStackTrace();
            return 0L;
        }
    }

    /**
     * 创建文档
     * @param index
     * @return
     */
    public Long addDocument(String index, String id, String docJSONStr) {
        try {
            //log.info("添加es文档，索引为:{}，文档数据:{}",index,docJSONStr);
            IndexResponse response = client.index(x -> x.index(index).id(id).document(docJSONStr));
            return response.version();
        } catch (IOException e) {
            e.printStackTrace();
            return 0L;
        }
    }

    /**
     * 根据id查询文档
     * @param index
     * @return
     */
    public <T> T getDocument(String index, String id, Class<T> c) {
        try {
            GetResponse<T> response = client.get(x -> x.index(index).id(id), c);
            return response.source();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根据id修改文档
     * @param index
     * @return
     */
    public boolean modifyDocumentById(String index, String id, Map<String, Object> doc) {
        try {
            log.info("修改es文档，索引为:{}，id为:{}，文档数据:{}",index,id,JSON.toJSONString(doc));
            UpdateResponse<Object> response = client.update(builder -> builder.index(index).id(id).doc(doc), Object.class);
            return response.version() > 0;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 查询索引下所有文档
     * @param index
     * @return
     */
    public <T> List<Hit<T>> allDocments(String index, Class<T> t) {
        try {
            SearchResponse<T> response = client.search(x -> x.index(index), t);
            return response.hits().hits();
        } catch (IOException e) {
            e.printStackTrace();
            return Collections.emptyList();
        }
    }

    /**
     * 搜索索引，query查询
     *
     * @param index
     * @return
     */
    public SearchResponse<Object> searchIndex(List<String> index, Query.Builder query, Integer from, Integer to, List<SortOptions> sorts, Highlight.Builder highlight) {
        SearchResponse<Object> search = null;
        try {
            SearchRequest.Builder builder = new SearchRequest.Builder();
            SearchRequest.Builder b = builder.ignoreUnavailable(true).index(index);
            if (query != null) {
                b.query(q -> query);
            }
            if (highlight != null) {
                b.highlight(h -> highlight);
            }
            if (from != null && to != null) {
                b.from(from).size(to);
            }
            if (sorts != null) {
                b.sort(sorts);
            }
            search = client.search(x -> b, Object.class);
            return search;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return search;
    }

    /**
     * 搜索索引，多条件bool查询,分页,排序
     * @param index
     * @return
     */
    public SearchResponse<Object> searchIndex(List<String> index, ESBuilder param, Integer from, Integer to, List<SortOptions> sorts, Highlight.Builder highlight) {
        SearchResponse<Object> search;
        try {
            SearchRequest.Builder builder = new SearchRequest.Builder();
            SearchRequest.Builder b = builder.ignoreUnavailable(true).index(index);
            b.trackTotalHits(new TrackHits.Builder().enabled(true).build());
            BoolQuery.Builder boo = null;
            if (param.getBool() != null) {
                boo = param.getBool();
            }
            if (param.getFilterQuerys() != null) {
                if (boo == null) {
                    boo = new BoolQuery.Builder();
                }
                boo.filter(param.getFilterQuerys());
            }
            if (boo != null) {
                BoolQuery.Builder paramBool = boo;
                b.query(q -> q.bool(bb -> paramBool));
            }
            if (from != null && to != null && from > -1 && to > 0) {
                b.from(from).size(to);
            }
            if (sorts != null) {
                b.sort(sorts);
            }
            if (highlight != null) {
                b.highlight(h -> highlight);
            }
            search = client.search(x -> b, Object.class);
            return search;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 搜索索引，多条件bool查询,不分页,排序
     * @param index
     * @return
     */
    public SearchResponse<Object> searchIndexAll(List<String> index, Query.Builder query) {
        SearchResponse<Object> search = null;
        try {
            SearchRequest.Builder builder = new SearchRequest.Builder();
            SearchRequest.Builder b = builder.ignoreUnavailable(true).index(index);
            if (query != null) {
                b.query(q -> query);
            }
            b.from(0).size(100000);
            search = client.search(x -> b, Object.class);
            return search;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return search;
    }

    /**
     * 搜索索引，多条件bool查询,分页,排序,分组
     * @param index
     * @return
     */
    public Map<String, Aggregate> searchIndex(List<String> index, BoolQuery.Builder boo, Integer size, List<SortOptions> sorts, Map<String, Aggregation> group) {
        SearchResponse<Object> search;
        try {
            SearchRequest.Builder builder = new SearchRequest.Builder();
            SearchRequest.Builder b = builder.ignoreUnavailable(true).index(index);
            if (boo != null) {
                b.query(q -> q.bool(t -> boo));
            }
            if (size != null && size > 0) {
                b.size(size);
            }
            if (group != null && !group.isEmpty()) {
                b.aggregations(group);
            }
            if (sorts != null) {
                b.sort(sorts);
            }
            search = client.search(x -> b, Object.class);
            return search.aggregations();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Collections.emptyMap();
    }

    /**
     * 搜索索引，无条件,分页,排序
     *
     * @param index
     * @return
     */
    public SearchResponse<Object> searchIndex(List<String> index, Integer from, Integer to, List<SortOptions> sorts) {
        SearchResponse<Object> search = null;
        try {
            SearchRequest.Builder builder = new SearchRequest.Builder();
            SearchRequest.Builder b = builder.ignoreUnavailable(true).index(index);
            if (from != null && to != null && from > -1 && to > 0) {
                b.from(from).size(to);
            }
            if (sorts != null) {
                b.sort(sorts);
            }
            return client.search(x -> b, Object.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return search;
    }

    /**
     * 搜索索引，无条件,分组,排序,分组
     * @param index
     * @return
     */
    public Map<String, Aggregate> searchIndex(List<String> index, Integer size, List<SortOptions> sorts, Map<String, Aggregation> group) {
        SearchResponse<Object> search;
        try {
            SearchRequest.Builder builder = new SearchRequest.Builder();
            SearchRequest.Builder b = builder.ignoreUnavailable(true).index(index)
                    .size(size);
            if (group != null && !group.isEmpty()) {
                b.aggregations(group);
            }
            if (sorts != null) {
                b.sort(sorts);
            }
            search = client.search(x -> b, Object.class);
            return search.aggregations();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Collections.emptyMap();
    }


    /**
     * 搜索索引里的某一个文档
     *
     * @param index
     * @return
     */
    public Map<String, Object> searchIndexDoc(String index, String docId) {
        GetResponse<Map> search;
        try {
            search = client.get(x -> x.index(index).id(docId), Map.class);
            return search.source();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Collections.emptyMap();
    }

    /**
     * 获取索引下的所有字段
     * @param index
     * @return
     */
    public Set<String> getMapping(String index) {
        GetIndexResponse response = null;
        try {
            response = client.indices().get(b -> b.index(index));
            Map<String, Property> properties = response.result().get(index).mappings().properties();
            return properties.keySet();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Collections.emptySet();
    }

    /**
     * 新建字段
     * @param index
     * @return
     */
    public boolean addMapping(String index, Map<String, Property> mapping) {
        PutMappingResponse response = null;
        try {
            response = client.indices().putMapping(typeMappingBuilder -> typeMappingBuilder
                    .index(index)
                    .properties(mapping)
            );
            return response.acknowledged();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }




}
